package com.xaaef.authorize.server.service.impl;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.xaaef.authorize.common.domain.ClientDetails;
import com.xaaef.authorize.common.domain.TokenValue;
import com.xaaef.authorize.common.domain.UserInfo;
import com.xaaef.authorize.common.enums.GrantType;
import com.xaaef.authorize.common.enums.OAuth2Error;
import com.xaaef.authorize.common.enums.UserStatus;
import com.xaaef.authorize.common.exception.OAuth2Exception;
import com.xaaef.authorize.server.params.TencentModeParam;
import com.xaaef.authorize.server.props.OAuth2ServerProperties;
import com.xaaef.authorize.server.repository.ClientDetailsRepository;
import com.xaaef.authorize.server.repository.UserInfoRepository;
import com.xaaef.authorize.server.service.TencentAuthorizeService;
import com.xaaef.authorize.server.service.TokenCacheManager;
import com.xaaef.authorize.server.token.OAuth2Token;
import com.xaaef.authorize.server.util.JwtTokenUtils;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * All rights Reserved, Designed By 深圳市铭灏天智能照明设备有限公司
 * <p>
 * </p>
 *
 * @author Wang Chen Chen
 * @version 1.0.1
 * @date 2021/8/5 15:41
 * @copyright 2021 http://www.mhtled.com Inc. All rights reserved.
 */

@Slf4j
@Service
public class TencentAuthorizeServiceImpl extends DefaultAuthorizeService implements TencentAuthorizeService {

    private RestTemplate restTemplate;

    public TencentAuthorizeServiceImpl(
            ClientDetailsRepository clientDetailsRepository,
            UserInfoRepository userInfoRepository,
            TokenCacheManager cacheManager,
            JwtTokenUtils jwtTokenUtils,
            RestTemplate restTemplate
    ) {
        super(clientDetailsRepository, userInfoRepository, cacheManager, jwtTokenUtils);
        this.restTemplate = restTemplate;
    }

    @Override
    public String buildWeChatAuthAddress(TencentModeParam param) throws OAuth2Exception {
        ClientDetails client = validateClient(
                param.getClientId(),
                param.getClientSecret(),
                GrantType.WE_CHAT,
                param.getGrantType()
        );

        return null;
    }

    @Override
    public String buildQQAuthAddress(TencentModeParam param) throws OAuth2Exception {
        // 校验客户端信息
        validateClient(
                param.getClientId(),
                param.getClientSecret(),
                GrantType.TENCENT_QQ,
                param.getGrantType()
        );

        String state = jwtTokenUtils.createUUID();

        OAuth2ServerProperties.TencentQQ qq = getProps().getQq();
        String redirectRri = URLEncoder.encode(qq.getRedirectUri(), StandardCharsets.UTF_8);
        var link = new StringBuilder("https://graph.qq.com/oauth2.0/authorize?response_type=code")
                .append("&client_id=").append(qq.getAppId())
                .append("&redirect_uri=").append(redirectRri)
                .append("&state=").append(state)
                .append("&scope=get_user_info");
        // 讲当前客户端保存到缓存中！
        cacheManager.setTencentMode(state, param);
        return link.toString();
    }

    @Override
    public OAuth2Token QqAuthorize(String code, String state) throws OAuth2Exception {
        var param = cacheManager.getTencentMode(state);
        if (param == null || StringUtils.isBlank(param.getClientId())) {
            throw new OAuth2Exception(OAuth2Error.CLIENT_INVALID);
        }
        OAuth2ServerProperties.TencentQQ qq = getProps().getQq();
        String redirectRri = URLEncoder.encode(qq.getRedirectUri(), StandardCharsets.UTF_8);
        var link = new StringBuilder("https://graph.qq.com/oauth2.0/token?grant_type=authorization_code")
                .append("&client_id=").append(qq.getAppId())
                .append("&client_secret=").append(qq.getAppSecret())
                .append("&code=").append(code)
                .append("&redirect_uri=").append(redirectRri)
                .append("&fmt=json");

        log.info("获取 accessToken : {}", link);

        QqOAuth2Token auth2Token = restTemplate.getForObject(link.toString(), QqOAuth2Token.class);
        if (StringUtils.isBlank(auth2Token.getAccessToken())) {
            throw new OAuth2Exception(OAuth2Error.OAUTH2_EXCEPTION);
        }
        var unionidLink = new StringBuilder("https://graph.qq.com/oauth2.0/me")
                .append("?access_token=").append(auth2Token.getAccessToken())
                .append("&unionid=1")
                .append("&fmt=json");
        log.info("获取 unionid : {}", unionidLink);
        var unionMap = restTemplate.getForObject(link.toString(), Map.class);
        String clientId = String.valueOf(unionMap.get("client_id"));
        String openId = String.valueOf(unionMap.get("openid"));
        String unionId = String.valueOf(unionMap.get("unionid"));
        log.info("unionId: {}  clientId: {}  openId: {} ", unionId, clientId, openId);

        UserInfo userInfo = validateUserInfo(unionId, GrantType.TENCENT_QQ);
        ClientDetails client = getClient(param.getClientId());
        // 生成 唯一的 认证id
        String tokenId = jwtTokenUtils.createTokenId();
        // 服务端 token
        TokenValue tokenValue = TokenValue.builder()
                .tokenId(tokenId)
                .client(client)
                .user(userInfo)
                .grantType(GrantType.TENCENT_QQ)
                .build();
        setTokenCache(tokenId, tokenValue);
        return buildToken(tokenId, client.getScope());
    }


    private UserInfo validateUserInfo(String unionId, GrantType grantType) throws OAuth2Exception {
        UserInfo userInfo = userInfoRepository.selectBySocialOpenId(unionId, grantType.getCode());
        if (userInfo == null) {
            throw new OAuth2Exception(OAuth2Error.USER_INVALID);
        }
        if (UserStatus.DISABLE == userInfo.getStatus()) {
            throw new OAuth2Exception(OAuth2Error.USER_DISABLE);
        }
        if (UserStatus.LOCKING == userInfo.getStatus()) {
            throw new OAuth2Exception(OAuth2Error.USER_LOCKING);
        }
        return userInfo;
    }

    @Getter
    @Setter
    @ToString
    static class QqOAuth2Token {

        /**
         * 接口调用有错误时，会返回code和msg字段，以url参数对的形式返回，value部分会进行url编码（UTF-8）。
         * <p>
         * https://wiki.connect.qq.com/%E5%85%AC%E5%85%B1%E8%BF%94%E5%9B%9E%E7%A0%81%E8%AF%B4%E6%98%8E#100000-100031.EF.BC.9APC.E7.BD.91.E7.AB.99.E6.8E.A5.E5.85.A5.E6.97.B6.E7.9A.84.E5.85.AC.E5.85.B1.E8.BF.94.E5.9B.9E.E7.A0.81
         */
        @JsonProperty("code")
        private String code;

        /**
         * 授权令牌，Access_Token。
         */
        @JsonProperty("msg")
        private String msg;

        /**
         * 授权令牌，Access_Token。
         */
        @JsonProperty("access_token")
        private String accessToken;

        /**
         * 该access token的有效期，单位为秒。
         */
        @JsonProperty("expires_in")
        private Integer expiresIn;

        /**
         * 在授权自动续期步骤中，获取新的Access_Token时需要提供的参数。
         * 注：refresh_token仅一次有效
         */
        @JsonProperty("refresh_token")
        private String refreshToken;

    }


}
